package com.youlai.system.service;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.youlai.system.common.enums.DegreeEnum;
import com.youlai.system.common.enums.ExamTypeEnum;
import com.youlai.system.common.enums.QuotaEnum;
import com.youlai.system.model.entity.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 *
 */
@Component
@RequiredArgsConstructor
@Slf4j
public class DegreeDataExecService {

    private final SysScoreService scoreService;

    private final SysDegreeService degreeService;

    private final SysCourseService courseService;

    private final SysPeriodService periodService;

    private final SysExamService examService;

    public static final LocalDateTime MAX_TIME = DateUtil.parseLocalDateTime("2024-02-19 00:00:00");

    private final SysUserService userService;

    private final SysFrequencyService frequencyService;

    private final SysMoldService moldService;

    public void execAllStuAllCourseExam(Long deptId) {
        List<Long> userIdList = deptId != null ? userService.idListByDeptId(deptId) : userService.idList();
        List<Long> courseIdList = courseService.idList();
        userIdList.forEach(userId -> {
            courseIdList.forEach(courseId -> {
                execSingleStuCourseExam(userId, courseId);
            });
        });
    }

    public void execResetAllStuAllCourseExam(Long deptId) {
        List<Long> userIdList = deptId != null ? userService.idListByDeptId(deptId) : userService.idList();
        List<Long> courseIdList = courseService.idList();
        userIdList.forEach(userId -> {
            courseIdList.forEach(courseId -> {
                execResetExamPeriodDegreeScore(userId, courseId);
            });
        });
    }

    public void execResetExamPeriodDegreeScore(Long studentId, Long courseId) {
        examService.clearExamBy(studentId, courseId);
        periodService.clearPeriodBy(studentId, courseId);
        degreeService.clearDegreeBy(studentId, courseId);
        scoreService.updateHasUsedToZero(studentId, courseId);
    }

    @Async
    public void execResetExamPeriodDegreeScore(Long courseId) {
        examService.clearExamByCourseId(courseId);
        periodService.clearPeriodByCourseId(courseId);
        degreeService.clearDegreeByCourseId(courseId);
        scoreService.updateHasUsedToZeroByCourseId(courseId);
        List<Long> userIdList = userService.idList();
        userIdList.forEach(userId -> {
            execSingleStuCourseExam(userId, courseId);
        });
    }

    public void examAHandle(SysExam exam, LocalDateTime lastTime, Integer month) {
        LocalDateTime nextScoreTime = lastTime.plusMonths(month);
        exam.setPrevScoreTime(lastTime);
        exam.setNextScoreTime(nextScoreTime);
        exam.setEvaluateTimes(1);
    }

    public void examBHandle(SysExam exam, LocalDateTime lastTime, Integer month) {
        LocalDateTime nextScoreTime = lastTime.plusMonths(month);
        exam.setPrevScoreTime(lastTime);
        exam.setNextScoreTime(nextScoreTime);
        exam.setEvaluateTimes(2);
    }

    public void examCHandle(SysExam exam, LocalDateTime lastTime, Integer month) {
        LocalDateTime nextScoreTime = lastTime.plusMonths(month);
        exam.setPrevScoreTime(lastTime);
        exam.setNextScoreTime(nextScoreTime);
        exam.setEvaluateTimes(3);
    }

    public void examHandle(SysExam exam, Integer degree, LocalDateTime lastTime, Integer month) {
        if (DegreeEnum.A.getValue().equals(degree)) {
            //级别为A的情况（每个周期都要考，考1次）
            examAHandle(exam, lastTime, month);
        } else if (DegreeEnum.B.getValue().equals(degree)) {
            //级别为B的情况（每个周期都要考，考2次）
            examBHandle(exam, lastTime, month);
        } else if (DegreeEnum.C.getValue().equals(degree)) {
            //级别为B的情况（每个周期都要考，考3次）
            examCHandle(exam, lastTime, month);
        }
        examService.saveOrUpdate(exam);
    }

    public void recycleExecPeriod(Long studentId, Long courseId) {
        do {
            execSingleStuCoursePeriod(studentId, courseId);
            List<SysScore> scoreList = scoreService.getScoreList(studentId, courseId, null);
            if (CollectionUtil.isEmpty(scoreList)) {
                break;
            }
            SysDegree degree = degreeService.getStuCourseDegree(studentId, courseId);
            List<SysPeriod> periodList = periodService.getPeriodList(studentId, courseId);
            if (degree != null && CollectionUtil.isEmpty(periodList)) {
                if (DegreeEnum.B.getValue().equals(degree.getDegree()) && scoreList.size() < 2) {
                    break;
                }

                if (DegreeEnum.C.getValue().equals(degree.getDegree()) && scoreList.size() < 3) {
                    break;
                }
            }
            if (degree != null && CollectionUtil.isNotEmpty(periodList)) {
                SysPeriod period = periodList.get(0);

                if (DegreeEnum.B.getValue().equals(period.getDegree()) && scoreList.size() < 2) {
                    break;
                }

                if (DegreeEnum.C.getValue().equals(period.getDegree()) && scoreList.size() < 3) {
                    break;
                }
            }

        } while (true);
    }

    /**
     * 考试名单生成
     *
     * @param studentId
     * @param courseId
     */
    public void execSingleStuCourseExam(Long studentId, Long courseId) {
        //先定级后计算考试名单
        recycleExecPeriod(studentId, courseId);
        //获取初级定级
        SysDegree degree = degreeService.getStuCourseDegree(studentId, courseId);
        //获取周期定级倒序列表，第一个即为这个人这门课最新的等级状态
        List<SysPeriod> periodList = periodService.getPeriodList(studentId, courseId);
        if (degree == null && CollectionUtil.isEmpty(periodList)) {
            //如果初级定级和周期定级为空，就返回（说明这个人从来没有参加这门课的考试）
            return;
        }

        //获取评估频次过程
        SysUser user = userService.getById(studentId);
        if (user == null || user.getGradeId() == null) {
            return;
        }

        SysFrequency frequency = frequencyService.getOneByGradeIdAndCourseId(user.getGradeId(), courseId);
        if (frequency == null || frequency.getMoldId() == null) {
            return;
        }

        SysMold mold = moldService.getById(frequency.getMoldId());
        if (mold == null || mold.getFrequency() == null) {
            return;
        }

        Integer month = mold.getFrequency();

        SysExam exam = examService.getExam(studentId, courseId);
        if (exam == null) {
            exam = new SysExam();
            exam.setStudentId(studentId);
            exam.setCourseId(courseId);
        }

        exam.setCourseMonth(month);

        if (degree != null && CollectionUtil.isEmpty(periodList)) {
            //初始定级在，但是周期定级不存在，就取初始定级的数据
            exam.setLastDegree(degree.getDegree());
            exam.setPeriodDegreeId(degree.getId());
            exam.setPeriodDegreeType(ExamTypeEnum.DEGREE.getValue());
            examHandle(exam, degree.getDegree(), degree.getLastTime(), month);
        } else if (degree != null && CollectionUtil.isNotEmpty(periodList)) {
            //初始定级在，但是周期定级也存在，就取周期定级的数据（最新值）
            SysPeriod period = periodList.get(0);
            exam.setLastDegree(period.getDegree());
            exam.setPeriodDegreeId(period.getId());
            exam.setPeriodDegreeType(ExamTypeEnum.PERIOD.getValue());
            examHandle(exam, period.getDegree(), period.getLastTime(), month);
        }
    }

    /**
     * 周期定级
     *
     * @param studentId
     * @param courseId
     */
    public void execSingleStuCoursePeriod(Long studentId, Long courseId) {
        //第一步去做初始定级，防止没有初始定级数据
        execSingleStuCourseDegree(studentId, courseId);

        SysDegree degree = degreeService.getStuCourseDegree(studentId, courseId);
        if (degree == null) {
            //没有初级定级的情况返回
            return;
        }

        List<SysPeriod> periodList = periodService.getPeriodList(studentId, courseId);
        SysPeriod period = new SysPeriod();
        period.setStudentId(studentId);
        period.setCourseId(courseId);
        List<SysScore> scoreList = null;
        Integer lastDegreeValue = CollectionUtil.isEmpty(periodList) ? degree.getDegree() : periodList.get(0).getDegree();
        Integer num = CollectionUtil.isEmpty(periodList) ? 1 : (periodList.get(0).getPeriod() + 1);
        period.setPeriod(num);
        if (DegreeEnum.A.getValue().equals(lastDegreeValue)) {
            //上一周期级别是A类人员的情况
            scoreList = scoreService.getNumScoreList(period.getStudentId(), period.getCourseId(), 1);
            if (CollectionUtil.isEmpty(scoreList)) {
                return;
            }
        } else if (DegreeEnum.B.getValue().equals(lastDegreeValue)) {
            //上一周期级别是B类人员的情况
            scoreList = scoreService.getNumScoreList(period.getStudentId(), period.getCourseId(), 2);
            if (CollectionUtil.isEmpty(scoreList) || scoreList.size() < 2) {
                return;
            }
        } else if (DegreeEnum.C.getValue().equals(lastDegreeValue)) {
            //上一周期级别是C类人员的情况
            scoreList = scoreService.getNumScoreList(period.getStudentId(), period.getCourseId(), 3);
            if (CollectionUtil.isEmpty(scoreList) || scoreList.size() < 3) {
                return;
            }
        }
        period.setScoreId(scoreList.get(scoreList.size() - 1).getId());
        period.setScore(scoreList.get(scoreList.size() - 1).getScore());
        period.setScoreCount(scoreList.size());
        period.setScoreList(StrUtil.join(StrUtil.COMMA, scoreList.stream().map(SysScore::getId).collect(Collectors.toList())));
        period.setLastTime(scoreList.get(scoreList.size() - 1).getRealTime());
        period.setDegree(judgeDegreeFrom(scoreList));
        periodService.save(period);
        scoreList.forEach(sysScore -> sysScore.setHasUsed(1));
        scoreService.updateBatchById(scoreList);

    }

    /**
     * 初始定级
     *
     * @param studentId
     * @param courseId
     */

    public void execSingleStuCourseDegree(Long studentId, Long courseId) {
        List<SysScore> scoreList = scoreService.getScoreList(studentId, courseId, MAX_TIME);
        SysDegree degree = degreeService.getStuCourseDegree(studentId, courseId);
        if (CollectionUtil.isEmpty(scoreList)) {
            // 如果这个人这门课在成绩表没有对应的记录，就说明从来考核过，就不做任何处理
            if (degree == null) {
                scoreList = scoreService.getScoreList(studentId, courseId, null);
                if (CollectionUtil.isEmpty(scoreList)) {
                    return;
                }
                scoreList = scoreList.subList(0, 1);
            }
        }

        if (degree == null) {
            degree = new SysDegree();
            degree.setStudentId(studentId);
            degree.setCourseId(courseId);
            degree.setScoreCount(scoreList.size());
            degree.setScoreList(StrUtil.join(StrUtil.COMMA, scoreList.stream().map(SysScore::getId).collect(Collectors.toList())));
            degree.setMonth(scoreList.get(0).getFrequency());//频次
            degree.setDegree(judgeDegreeFrom(scoreList));//定级（A,B,C）方案
            degree.setLastTime(scoreList.get(0).getRealTime());
            degree.setLastScoreId(scoreList.get(0).getId());
            degreeService.save(degree);
            scoreList.forEach(sysScore -> {
                sysScore.setHasUsed(1);
            });
            scoreService.updateBatchById(scoreList);
        }
    }

    Integer judgeDegreeFrom(List<SysScore> sysScoreList) {
        List<Integer> scoreList = sysScoreList.stream().map(SysScore::getScore).distinct().toList();//分数
        List<Integer> questionCountList = sysScoreList.stream().map(SysScore::getQuestionCount).distinct().toList();//问题项数
        List<Integer> quotaList = sysScoreList.stream().map(SysScore::getQuota).distinct().toList();//指标类（无，不影响指标类，影响指标类）

        //定级A的判定条件
        if (scoreList.size() == 1 && Objects.equals(scoreList.get(0), 100) && questionCountList.size() == 1 && Objects.equals(questionCountList.get(0), 0) &&
                quotaList.size() == 1 && Objects.equals(quotaList.get(0), QuotaEnum.NIL.getValue())) {
            return DegreeEnum.A.getValue();
        }

        if (quotaList.contains(QuotaEnum.YES.getValue()) || questionCountList.stream().anyMatch(it -> it >= 3)) {
            return DegreeEnum.C.getValue();
        }

        return DegreeEnum.B.getValue();
    }
}
